#include "doom.h"
#include "wall.h"
#include "screenshot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <GL/glut.h>


int moving, startx, starty;

#ifndef M_PI
#define M_PI 3.14159265f
#endif
#define FOG_START 1.0
#define FOG_DENSITY 0.1
#define FOG_END 10.0

GLfloat eye   [3]; /* where the viewer is */
GLfloat target[3]; /* the target point */ 
GLint wposx = 300;
GLint wposy = 300; 
GLint wsizex = 800;
GLint wsizey = 600;

GLfloat fov = 60.0;/* fov field of view in degree */
GLfloat ar = 1.0;/* ar aspect ratio */
GLfloat znear = 0.01; /* znear Z near */
GLfloat zfar = 300.0;/* zfar Z far */

GLfloat slide = 0.0;
GLfloat slide_step = 0.1;


GLfloat rate_angle = 0.6;
GLfloat rate_move = 0.40;

/* ---------------------------------------------------------- */

active_square_t* asMap;
room_t* roomMap;

int curtexture;
int mapenable;
int fullscreen;
int fog_enable;
int run_enable;
int light_enable;
int shadow_enable;
int fog_mode;

/* ---------------------------------------------------------- */

struct wall_t* last_crossed_wall=0;

/* ---------------------------------------------------------- */
int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH|GLUT_RGBA);
	glutInitWindowPosition(wposx,wposy);
	glutInitWindowSize(wsizex, wsizey);
	glutCreateWindow("OGLDOOM");
	
	/* Register GLUT callbacks. */
	glutDisplayFunc(redraw);
	glutKeyboardFunc(normalKey);
	glutSetKeyRepeat(GLUT_KEY_REPEAT_ON);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutSpecialFunc(key);
	glutReshapeFunc(changeSize);
	/*cb idle*/
	glutIdleFunc(idle);
	init();

	
	glMatrixMode(GL_PROJECTION);
	gluPerspective
	( 
			fov,/* fov field of view in degree */
			ar,/* ar aspect ratio */
			znear, /* znear Z near */
			zfar/* zfar Z far */
	);
	//printf("Entering in main loop\n");
	
	glutMainLoop();
	return 0; /* ANSI C requires main to return int. */
}

void init(void){
		//printf("initializing doom\n");
	    initLookAt();
		mapenable = 0;
		fullscreen = 0;
		glEnable(GL_DEPTH_TEST);
		fog_enable = 0;
		run_enable = 0;
		light_enable = 0;
		shadow_enable = 0;
		initPly();		
		initLight();
		initTextures();
		asMap = initASMap();
		roomMap = initRooms();
		//printf("doom initialized\n");
}


//per gestire il resize della finestra
void changeSize(int w, int h)
	{
/*Impedisce di incorrere in una divisione x zero nel resize della finestra*/
 	if(h == 0) h = 1;
	ar = 1.0f * w / h;
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
   	glViewport(0, 0, w, h);
	gluPerspective
	( 
			fov,/* fov field of view in degree */
			ar,/* ar aspect ratio */
			znear, /* znear Z near */
			zfar/* zfar Z far */
	);
}

void normalKey(unsigned char c, int x, int y) {
	
	int sq;
	switch (c){
	
		case 27:
		case 'q': 
		case 'Q':
			exit(0);
		break;
	/*movement*/
	
		case  'a':
		case  'w':
		case  's':
			move(c);
		break;	
		case  'd':
			c='x';
			move(c);
		break;	
		case 'l':
			light_enable=1-light_enable;
			toggleLight(isInRoom(roomMap, eye[0], eye[1])-1);
			//printf("Ligh_enable= %d\n", light_enable);
			
			if(light_enable==0)
				disableAllLights();
		break;
		case  'm':
			mapenable = 1 - mapenable;
		break;	

		case  'r':
			toggleRun();
		break;	
		case  'p':
			screenshot();
		break;	
		case  'n':
			fog_mode = (fog_mode + 1) % 3;
			printf("Valore di fogmode = %d\n",fog_mode);
		break;	
		case  'f':
			fullscreen = 1 - fullscreen;
			if (fullscreen)
				glutFullScreen();
			else{
				glutPositionWindow(wposx,wposy);
				glutReshapeWindow(wsizex,wsizey);
			} 	
		break;	
		case  ' ':
			sq = isInActiveSquare(asMap,eye[0],eye[1]);
			if(sq>=0){
				asMap[sq].activation = 1 - asMap[sq].activation ;
			}
		break;	
	
	}//switch
}

/* ---------------------------------------------------------- */
void redraw(void)
{
	int i;
	float angle;
	glClearColor(0.0,0.0,0.05,1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	slide+=slide_step;
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glEnable(GL_TEXTURE_2D);
	glPushMatrix();
		gluLookAt(
			eye[0],eye[1],eye[2],  /* eye  */
			target[0],target[1],target[2],  /* center  */
			0.0, 0.0, 1.0); /* up is in positive Y direction */
		drawAxes();
		disegnaPavimentiSoffitti();
		/*MURO PRINCIPALE*/
		disegnaMuroPrincipale();
		drawActiveSquares(asMap);
		fogEnable(fog_mode,eye, target);
		flashLight(eye[0],eye[1],eye[2],target[0],target[1],target[2]);
		glEnable(GL_TEXTURE_2D);
		drawObject(); 
		
	glPopMatrix();
	
/*		impostazione della look at per disegnare la mappa	*/
		glDisable(GL_DEPTH_TEST);
			glDisable(GL_TEXTURE_2D);
			glMatrixMode(GL_PROJECTION);
			glPushMatrix();
				glLoadIdentity();
				glOrtho(-200, 200, -200, 200, 0, 100);	
				gluLookAt(	100 , 100, 30,  /* eye  */100,100,0,  /* center  */0.0, 1.0, 0.0); /* up is in positive Y direction */
					if (mapenable){
						materials(&biancoEmittivo);
						disegnaMappa(eye[0],eye[1],eye[2],target[0],target[1],target[2]);
					}
					glPushMatrix();
					/*	mirino	*/
						materials(&biancoEmittivo);
						glBegin(GL_LINES);
							glVertex3f(95, 100, 0);	glVertex3f(105, 100, 0);
							glVertex3f(100, 95, 0);	glVertex3f(100, 105, 0);
						glEnd();
					glPopMatrix();
			glPopMatrix();						
			materials(&grigioOpaco);
			
			glMatrixMode(GL_MODELVIEW);
			glEnable(GL_TEXTURE_2D);
		glEnable(GL_DEPTH_TEST);
/*	fine seconda lookat		*/
	glutSwapBuffers();
}

/* ---------------------------------------------------------- */
void key(int c, int x, int y)
{

   /* move forward */
	if (c==GLUT_KEY_UP 
		|| c==GLUT_KEY_DOWN
		|| c==GLUT_KEY_RIGHT 
		|| c==GLUT_KEY_LEFT
		|| c==GLUT_KEY_PAGE_UP 
		|| c==GLUT_KEY_PAGE_DOWN
		)
	{
		move(c);
	}

  
  glutPostRedisplay();
}

void mouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON){ 
		startx = x;
		starty = y;
	}
}



void motion (int x, int y){
		float direction[3],direction_n[3],n;
		int deltax = (x - startx);
		int deltay = (y - starty);
		float angle=-1.0*rate_angle*deltay*M_PI/180.0f;
		float angle2=-1.0*rate_angle*deltax*M_PI/180.0f;

			direction[0]=target[0]-eye[0];
			direction[1]=target[1]-eye[1];
			direction[2]=target[2]-eye[2];

		float X=direction[0]*(float)cos(angle2)-direction[1]*(float)sin(angle2);
		float Y=direction[1]*(float)cos(angle2)+direction[0]*(float)sin(angle2);

			target[0]=eye[0]+X;
			target[1]=eye[1]+Y;
			target[2]=target[2]+3.0*angle;
			startx = x;starty = y;
	
}


void move(unsigned char c){
	int i;
	float new_position[3];
	float direction[3], direction_n[3], dirn[3], n;
	direction[0]=target[0]-eye[0];
	direction[1]=target[1]-eye[1];
	direction[2]=target[2]-eye[2];
	n=(float)sqrt(direction[0]*direction[0]+direction[1]*direction[1]+direction[2]*direction[2]);
	direction_n[0]=direction[0]/n;
	direction_n[1]=direction[1]/n;
	direction_n[2]=direction[2]/n;
	float sign;

   /* move forward */
	switch(c){
		case 'w':
		case 's':
			sign=c=='w'?+1.0f:-1.0f;
			new_position[0]=eye[0]+sign*rate_move*direction_n[0];
			new_position[1]=eye[1]+sign*rate_move*direction_n[1];
			new_position[2]=eye[2]+sign*rate_move*direction_n[2];
			
			for (i=0;i<NUMBER_OF_WALLS;i++)
			{
				if ((acrossWall(&walls[i],eye,new_position, last_crossed_wall)))
				{
					glutPostRedisplay();
					return;
				}
			}
			for (i=0;i<NUMBER_OF_DOORS;i++)
			{
				if ((acrossDoor(i,eye,new_position)))
				{
					glutPostRedisplay();
					return;
				}
			}
				/*spengo la luce dalla stanza da cui vengo
				 * e accendo quella della stanza in cui vado*/
			if(light_enable) {
				disableLightLastRoom(roomMap, eye, new_position);
				enableLightNextRoom(roomMap, eye, new_position);
			}
			eye[0]=eye[0]+sign*rate_move*direction_n[0];;target[0]=eye[0]+direction[0];
			eye[1]=eye[1]+sign*rate_move*direction_n[1];;target[1]=eye[1]+direction[1];
			target[2]=eye[2]+direction[2];
			last_crossed_wall=0;
		break;
		/*Strafe mode*/
		case 'x':
		case 'a':
//			float sign;
			sign=c=='x'?+1.0f:-1.0f;
			direction[0] = target[0]-eye[0];
			direction[1] = target[1]-eye[1];
			direction[2] = target[2]-eye[2];
			n = (float)sqrt(direction[0]*direction[0]+direction[1]*direction[1]+direction[2]*direction[2]);
			dirn[0] = +1*direction[1]/n; 
			dirn[1] = -1*direction[0]/n;
			dirn[2] = direction[2]/n;
	
			new_position[0]=eye[0]+sign*1.5*dirn[0];
			new_position[1]=eye[1]+sign*1.5*dirn[1];
			new_position[2]=eye[2]+sign*1.5*dirn[2];
			for (i=0;i<NUMBER_OF_WALLS;i++)
			{
				if (acrossWall(&walls[i],eye,new_position, last_crossed_wall))
				{
					glutPostRedisplay();
					return;
				}
			}
			for (i=0;i<NUMBER_OF_DOORS;i++)
			{
				if ((acrossDoor(i,eye,new_position)))
				{
					glutPostRedisplay();
					return;
				}
			}
			if(light_enable) {
				disableLightLastRoom(roomMap, eye, new_position);
				enableLightNextRoom(roomMap, eye, new_position);
			}
			eye[0] 		+= sign*dirn[0];
			eye[1] 		+= sign*dirn[1];
			target[0]	+= sign*dirn[0];
			target[1]	+= sign*dirn[1];
			last_crossed_wall=0;
		break;
			/* rotate */
		case GLUT_KEY_RIGHT:
		case GLUT_KEY_LEFT:
		{
			const float angle=(c==GLUT_KEY_LEFT?1.0f : -1.0f)*3*M_PI/180.0f;
			float X=direction[0]*(float)cos(angle)-direction[1]*(float)sin(angle);
			float Y=direction[1]*(float)cos(angle)+direction[0]*(float)sin(angle);
			target[0]=eye[0]+X;
			target[1]=eye[1]+Y;
			last_crossed_wall=0;
		}
		break;
	 	/*view up and down*/
		case GLUT_KEY_UP:
		case GLUT_KEY_DOWN:
		{
			const float angle=(c==GLUT_KEY_UP?0.5f:-0.5f);
			target[2]=target[2]+angle;
			last_crossed_wall=0;
		}
		break;
		
	}//switch
  glutPostRedisplay();
}

/*------------------------------------------*/

void idle(){
	glutPostRedisplay();
}


void initLookAt(){
		eye[0]=70;
		eye[1]=50;
		eye[2]=2;
	
		target[0]=eye[0]+10;
		target[1]=eye[1]+10;
		target[2]=2;	
}

int initPly(){
	//printf("init ply\n");
	
	initMeshes();
	
	if (!open_ply("ply/torretta.ply",0))
	{
		fprintf(stderr,"Error opening %s file\n","ply/torretta.ply");
		return -1;
	}
	if (!open_ply("ply/samurai.ply",1))
	{
		fprintf(stderr,"Error opening %s file\n","ply/samurai.ply");
		return -1;
	}
	if (!open_ply("ply/tesla.ply",2))
	{
		fprintf(stderr,"Error opening %s file\n","ply/tesla.ply");
		return -1;
	}
}

void toggleRun(){
	run_enable = 1 - run_enable;
	if (run_enable){
		rate_move = 3.0*rate_move;		
	}else{
		rate_move = rate_move/3.0;
	}
}

